home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / quicktime / goodies / qtreadwritejpeg.win / qtreadwritejpeg.c next >
Encoding:
Text File  |  2000-06-23  |  20.6 KB  |  863 lines

  1. //////////
  2. //
  3. //    File:        QTReadWriteJPEG.c
  4. //
  5. //    Contains:    Sample code for compressing and decompressing JPEG images.
  6. //
  7. //    Written by:    Michael Marinkovich and Guillermo Ortiz
  8. //    Revised by:    Tim Monroe
  9. //                Based heavily on the existing "JPEG Sample" code.
  10. //
  11. //    Copyright:    © 1996-1998 by Apple Computer, Inc., all rights reserved.
  12. //
  13. //    Change History (most recent first):
  14. //
  15. //       <4>         02/03/99    rtm        reworked prompt and filename handling to remove "\p" sequences
  16. //       <3>         04/27/98    rtm        revised to uncouple this file from its Macintosh framework,
  17. //                                    to make the coding style conform to other code samples, and
  18. //                                    to make it run on Windows
  19. //       <2>         02/??/97    mwm        fixed some memory leaks; expanded JPEG parsing
  20. //       <1>         04/03/96    mwm        initial coding
  21. //       
  22. //    This sample code illustrates how to compress and decompress JPEG images using QuickTime. 
  23. //    We use the FCompressImage function, but you could also use the CompressImage function.
  24. //    Although this sample demonstrates only JPEG compression/decompression, you could use this
  25. //    as a framework for other types of compression (except for the decoding of the JPEG header).
  26. //
  27. //    In this sample code, we allow the user to open a JPEG image file; then we draw it into
  28. //    a window on the screen. Your application, of course, will probably want to do more
  29. //    interesting things with the image. We also allow the user to save an image using JPEG
  30. //    compression.
  31. //
  32. //    NOTES:
  33. //
  34. //    *** (1) ***
  35. //    You may incorporate this sample code into your applications without restriction, though
  36. //    the sample code has been provided "AS IS" and the responsibility for its operation is 100% yours.
  37. //    However, what you are not permitted to do is to redistribute the source as "DSC Sample Code"
  38. //    after having made changes. If you're going to redistribute the source, we require that you
  39. //    make it clear in the source that the code was descended from Apple Sample Code, but that
  40. //    you've made changes.
  41. //
  42. //////////
  43.  
  44. //////////
  45. //
  46. // header files
  47. //
  48. //////////
  49.  
  50. #include "QTReadWriteJPEG.h"
  51. #include "QTUtilities.h"
  52.  
  53.  
  54. //////////
  55. //
  56. // global variables
  57. //
  58. //////////
  59.  
  60. GWorldPtr                        gGWorld = NULL;            // the GWorld we load the image data into
  61. WindowPtr                        gImageWindow = NULL;    // the window we display the image in
  62.  
  63.  
  64. //////////
  65. //
  66. // QTJPEG_PromptUserForJPEGFileAndDisplay
  67. // Let the user select a JPEG image file, then display it in a window.
  68. //
  69. //////////
  70.  
  71. void QTJPEG_PromptUserForJPEGFileAndDisplay (void)
  72. {
  73.     SFTypeList                    myTypeList;
  74.     StandardFileReply            myReply;
  75.     PixMapHandle                myPixMap;    
  76.     Rect                        myRect;
  77.     OSErr                        myErr = noErr;
  78.     
  79.     // have the user select a JPEG image file
  80.     myTypeList[0] = kQTFileTypeJPEG;
  81.  
  82.     StandardGetFilePreview(NULL, 1, myTypeList, &myReply);
  83.     if (!myReply.sfGood)
  84.         goto bail;
  85.     
  86.     // read the file data into an offscreen graphics world
  87.     myErr = QTJPEG_ReadJPEG(myReply.sfFile, &gGWorld);
  88.     if (myErr != noErr)
  89.         goto bail;
  90.  
  91.     myRect = gGWorld->portRect;
  92.     myPixMap = GetGWorldPixMap(gGWorld);
  93.     if (LockPixels(myPixMap)) {
  94.  
  95.         // create a window to display the image in; then draw into that window        
  96.         MacOffsetRect(&myRect, 50, 50);
  97.         gImageWindow = NewCWindow(NULL, &myRect, myReply.sfFile.name, true, movableDBoxProc, (WindowPtr)-1L, true, 0);
  98.         if (gImageWindow == NULL)
  99.             goto bail;
  100.             
  101.         MacOffsetRect(&myRect, -50, -50);
  102.         
  103.         // copy the image from the offscreen port into the window
  104.         CopyBits(    (BitMapPtr)(*myPixMap),
  105.                     (BitMapPtr)(&(gImageWindow->portBits)),
  106.                     &myRect, 
  107.                     &gImageWindow->portRect,
  108.                     srcCopy, 
  109.                     NULL);
  110.     }
  111.  
  112. bail:
  113.     UnlockPixels(myPixMap);
  114. }
  115.  
  116.  
  117. //////////
  118. //
  119. // QTJPEG_ReadJPEG
  120. // Open a JPEG file with supplied FSSpec; the graphics world containing the image is returned through theGWorld.
  121. //
  122. //////////
  123.  
  124. OSErr QTJPEG_ReadJPEG (FSSpec theFile, CGrafPtr *theGWorld)
  125. {
  126.     ImageDescriptionHandle         myDesc;
  127.     Handle                        myData;
  128.     GWorldPtr                    myGWorld = NULL;
  129.     GWorldPtr                    oldWorld;
  130.     GDHandle                    oldGD;
  131.     PixMapHandle                myPixMap;
  132.     ICMDataProcRecord             myLoadProc;
  133.     Rect                        myRect;
  134.     DLDataRec                    myDataRec;
  135.     long                        mySize;
  136.     short                        myRefNum;
  137.     Ptr                            myDataPtr;
  138.     OSErr                        myErr = paramErr;
  139.     
  140.     // save the current graphics port
  141.     GetGWorld(&oldWorld, &oldGD);
  142.     
  143.     if (theGWorld != NULL) {
  144.             
  145.         myErr = FSpOpenDF(&theFile, fsRdWrShPerm, &myRefNum);
  146.         if (myErr == noErr) {
  147.         
  148.             myDesc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription));
  149.             if (myDesc != NULL) {
  150.                 HLock((Handle)myDesc);
  151.                 myErr = QTJPEG_ReadJPEGHeader(myRefNum, myDesc, &myRect);
  152.                 if (myErr == noErr) {
  153.                 
  154.                     myData = NewHandleClear(kBufferSize);
  155.                     myErr = MemError();
  156.                     
  157.                     if ((myData != NULL) && (myErr == noErr)) {
  158.                         myErr = QTJPEG_NewJPEGWorld(&myGWorld, (*myDesc)->depth, myRect);
  159.                         if ((myGWorld != NULL) && (myErr == noErr)){
  160.                             myErr = SetFPos(myRefNum, fsFromStart, 0);
  161.                             if (myErr == noErr) {
  162.                             
  163.                                 myPixMap = GetGWorldPixMap(myGWorld);
  164.                                 LockPixels(myPixMap);
  165.                                 SetGWorld(myGWorld, NULL);
  166.                                 HLock(myData);
  167.                                 
  168.                                 mySize = kBufferSize;
  169.                                 
  170.                                 // make sure the file size is greater than the buffer size
  171.                                 (void)GetEOF(myRefNum, &mySize);
  172.                                 if (kBufferSize > mySize)
  173.                                     mySize = mySize;
  174.                                     
  175.                                 myErr = FSRead(myRefNum, &mySize, *myData);
  176.                                 if (myErr == noErr) {
  177.                                     mySize = (*myDesc)->dataSize - kBufferSize;
  178.                                     if (mySize < 0) 
  179.                                         mySize = 0;
  180.                                         
  181.                                     myDataRec.fRefNum = myRefNum;
  182.                                     myDataRec.fFileLength = mySize;
  183.                                     myDataRec.fOrigPtr = *myData;
  184.                                     myDataRec.fEndPtr = *myData + kBufferSize;
  185.                                     myLoadProc.dataProc = NewICMDataProc(QTJPEG_DataLoadingProc);
  186.                                     myLoadProc.dataRefCon = (long)&myDataRec;
  187.                                     
  188.                                     myDataPtr = StripAddress(*myData);
  189.                                     
  190.                                     myErr = FDecompressImage(    myDataPtr,
  191.                                                                 myDesc,
  192.                                                                 myPixMap,
  193.                                                                 &myRect,
  194.                                                                 NULL,
  195.                                                                 srcCopy,
  196.                                                                 NULL,
  197.                                                                 NULL,
  198.                                                                 NULL,
  199.                                                                   codecHighQuality,
  200.                                                                   anyCodec,
  201.                                                                   kBufferSize,
  202.                                                                   &myLoadProc,
  203.                                                                   NULL);
  204.                                                           
  205.                                     DisposeRoutineDescriptor(myLoadProc.dataProc);
  206.                                      HUnlock(myData);
  207.                                     UnlockPixels(myPixMap);
  208.                                     
  209.                                     // restore the previous graphics world 
  210.                                     SetGWorld(oldWorld, oldGD);
  211.                                     
  212.                                     if (myErr == noErr)
  213.                                         *theGWorld = myGWorld;
  214.                                 }
  215.                             }
  216.                             
  217.                             if (myErr != noErr)
  218.                                 DisposeGWorld(myGWorld);
  219.                         }
  220.                         
  221.                         DisposeHandle(myData);
  222.                     }
  223.                 }
  224.                 
  225.                 HUnlock((Handle)myDesc);                
  226.                 DisposeHandle((Handle)myDesc);
  227.             }
  228.             
  229.             FSClose(myRefNum);                // close the file
  230.         }
  231.     }
  232.             
  233.     return(myErr);
  234. }
  235.  
  236.  
  237. //////////
  238. //
  239. // QTJPEG_ReadJPEGHeader
  240. // Read the JPEG header and fill out the specified ImageDescription with needed info.
  241. //
  242. //////////
  243.  
  244. OSErr QTJPEG_ReadJPEGHeader (short theRefNum, ImageDescriptionHandle theDesc, Rect *theRect)
  245. {
  246.     long                     mySize;
  247.     short                    mySkip;
  248.     UInt8                     myMarker;
  249.     Boolean                    isJFIF = false;
  250.     Boolean                    readingExtension = false;
  251.     OSErr                    myErr = noErr;
  252.     
  253.     // set file position to beginning of file
  254.     myErr = SetFPos(theRefNum, fsFromStart , 0);
  255.     if (myErr != noErr)
  256.         return(myErr);
  257.         
  258.     // get file length, so we don't overflow
  259.     myErr = GetEOF(theRefNum, &mySize);
  260.     if (myErr != noErr)
  261.         return(myErr);
  262.     
  263.     (*theDesc)->dataSize = mySize;
  264.  
  265.     // loop forever
  266.     while (true) {
  267.         myMarker = QTJPEG_FindNextMarker(theRefNum);
  268.         
  269.         switch (myMarker) {
  270.             case kSOIMarker:
  271.                 isJFIF = true;
  272.                 break;
  273.                 
  274.             case kAPPOMarker + 0:
  275.             case kAPPOMarker + 1:
  276.             case kAPPOMarker + 2:
  277.             case kAPPOMarker + 3:
  278.             case kAPPOMarker + 4:
  279.             case kAPPOMarker + 5:
  280.             case kAPPOMarker + 6:
  281.             case kAPPOMarker + 7:
  282.             case kAPPOMarker + 8:
  283.             case kAPPOMarker + 9:
  284.             case kAPPOMarker + 10:
  285.             case kAPPOMarker + 11:
  286.             case kAPPOMarker + 12:
  287.             case kAPPOMarker + 13:
  288.             case kAPPOMarker + 14:
  289.             case kAPPOMarker + 15:
  290.                 myErr = QTJPEG_HandleAPPOMarker(myMarker, theRefNum, theDesc, &readingExtension);
  291.                 if (myErr != noErr)
  292.                     return(myErr);
  293.                 break;
  294.                 
  295.             case kCommentMarker:
  296.                 QTJPEG_SkipLength(theRefNum);
  297.                 break;
  298.         
  299.             case kSOFMarker + 0:        // start of frame header marker
  300.             case kSOFMarker + 1:
  301.             case kSOFMarker + 2:
  302.             case kSOFMarker + 3:
  303.             case kSOFMarker + 5:
  304.             case kSOFMarker + 6:
  305.             case kSOFMarker + 7:
  306.             case kSOFMarker + 9:
  307.             case kSOFMarker + 10:
  308.             case kSOFMarker + 11:
  309.             case kSOFMarker + 13:
  310.             case kSOFMarker + 14:
  311.             case kSOFMarker + 15:
  312.                 myErr = QTJPEG_HandleSOFMarker(theRefNum, theDesc, readingExtension);
  313.                 if (myErr != noErr)
  314.                     return(myErr);
  315.                     
  316.                 if (!readingExtension) {
  317.                     MacSetRect(theRect, 0, 0, (*theDesc)->width, (*theDesc)->height);
  318.                     return(noErr);
  319.                 }
  320.                 break;
  321.                 
  322.             case kDACMarker:
  323.                 mySkip = QTJPEG_ReadWord(theRefNum) - 2;
  324.                 mySkip *= QTJPEG_ReadWord(theRefNum);
  325.                 myErr = SetFPos(theRefNum, fsFromMark, mySkip);
  326.                 break;
  327.                 
  328.             case kSOSMarker:
  329.                 QTJPEG_HandleSOSMarker(theRefNum);
  330.                 break;
  331.                 
  332.             case kDHTMarker:
  333.             case kDQTMarker:
  334.             case kRSTOMarker:
  335.                 QTJPEG_SkipLength(theRefNum);
  336.                 break;
  337.                 
  338.             case kEOIMarker:        // we reached the end of image
  339.                 // we are reading an extension so keep going
  340.                 if (readingExtension)
  341.                     readingExtension = false;
  342.                 break;
  343.         }
  344.     }
  345.     
  346.     return(myErr);
  347. }
  348.  
  349.  
  350. //////////
  351. //
  352. // QTJPEG_FindNextMarker
  353. // Find the next marker in the specified file.
  354. //
  355. //////////
  356.  
  357. UInt8 QTJPEG_FindNextMarker (long theRefNum)
  358. {
  359.     UInt8             myMarker;
  360.     
  361.     myMarker = QTJPEG_ReadByte(theRefNum);
  362.     
  363.     while (myMarker == kStartMarker)
  364.         myMarker = QTJPEG_ReadByte(theRefNum);
  365.  
  366.     while (myMarker == 0x00) {
  367.         myMarker = QTJPEG_ReadByte(theRefNum);
  368.  
  369.         while (myMarker != kStartMarker)
  370.             myMarker = QTJPEG_ReadByte(theRefNum);
  371.             
  372.         myMarker = QTJPEG_ReadByte(theRefNum);
  373.     }
  374.         
  375.     return(myMarker);
  376. }
  377.  
  378.  
  379. //////////
  380. //
  381. // QTJPEG_HandleAPPOMarker
  382. // Handle an APPO marker in the specified file.
  383. //
  384. //////////
  385.  
  386. OSErr QTJPEG_HandleAPPOMarker (UInt8 theMarker, long theRefNum, ImageDescriptionHandle theDesc, Boolean *readingExtension)
  387. {
  388.     Fixed            xRes, yRes;
  389.     long             myLength;
  390.     short            myUnits;
  391.     short            myVersion;
  392.     UInt8            myExtension;
  393.     UInt8             myType[5];
  394.     OSErr            myErr = noErr;
  395.  
  396.     // read skip bytes - header length - skip count
  397.     myLength = QTJPEG_ReadWord(theRefNum) - 2;
  398.     
  399.     if ((theMarker == kAPPOMarker) && (myLength >= 14)) {
  400.         myType[0] = QTJPEG_ReadByte(theRefNum);
  401.         myType[1] = QTJPEG_ReadByte(theRefNum);
  402.         myType[2] = QTJPEG_ReadByte(theRefNum);
  403.         myType[3] = QTJPEG_ReadByte(theRefNum);
  404.         myType[4] = QTJPEG_ReadByte(theRefNum);
  405.         
  406.         // check to see if we really have the JFIF header
  407.         if ((myType[0] == 'J') &&
  408.             (myType[1] == 'F') &&
  409.             (myType[2] == 'I') &&
  410.             (myType[3] == 'F')) {
  411.             
  412.               myVersion = QTJPEG_ReadWord(theRefNum);
  413.  
  414.             if (myVersion < 0x100)    
  415.                 return(paramErr);     // don't know this
  416.             else
  417.                 (*theDesc)->version = myVersion;
  418.                 
  419.             myUnits = QTJPEG_ReadByte(theRefNum);
  420.             xRes = QTJPEG_ReadWord(theRefNum);
  421.               yRes = QTJPEG_ReadWord(theRefNum);
  422.  
  423.             switch (myUnits) {
  424.                 case 0:            // no res, just aspect ratio
  425.                     xRes = FixMul(72L << 16, xRes << 16);
  426.                     yRes = FixMul(72L << 16, yRes << 16);
  427.                     break;
  428.                     
  429.                 case 1:            // dots per inch
  430.                     xRes = xRes << 16;
  431.                     yRes = yRes << 16;
  432.                     break;
  433.                     
  434.                 case 2:            // dots per centimeter (we convert to dpi )
  435.                     xRes = FixMul(0x28a3d, xRes << 16);
  436.                     yRes = FixMul(0x28a3d, xRes << 16);        // yRes?? RTM
  437.                     break;    
  438.                     
  439.                 default:
  440.                     break;
  441.             }
  442.             
  443.             (*theDesc)->hRes = xRes;
  444.             (*theDesc)->vRes = yRes;
  445.  
  446.             myLength -= 12;
  447.             myErr = SetFPos(theRefNum, fsFromMark, myLength);
  448.             
  449.         } else {
  450.             if ((myType[0] == 'J') &&
  451.                 (myType[1] == 'F') &&
  452.                 (myType[2] == 'X') &&
  453.                 (myType[3] == 'X')) { 
  454.                 
  455.                 *readingExtension = true;        // next markers are extensions; ignore them
  456.  
  457.                 myExtension = QTJPEG_ReadByte(theRefNum);
  458.                 switch (myExtension) {
  459.                     case 0x10:
  460.                     case 0x11:
  461.                     case 0x13:
  462.                         break;
  463.                     default:
  464.                         return(paramErr);
  465.                 }
  466.             }
  467.         }
  468.     } else
  469.         myErr = SetFPos(theRefNum, fsFromMark, myLength);
  470.  
  471.     return(myErr);
  472. }
  473.  
  474.  
  475. //////////
  476. //
  477. // QTJPEG_HandleSOFMarker
  478. // Handle an SOF marker in the specified file.
  479. //
  480. //////////
  481.  
  482. OSErr QTJPEG_HandleSOFMarker (long theRefNum, ImageDescriptionHandle theDesc, Boolean readingExtension)
  483. {
  484.     short             myWidth = 0;
  485.     short             myHeight = 0;
  486.     short             myComponents;
  487.     short            myLength;
  488.     StringPtr        myTitle = QTUtils_ConvertCToPascalString(kWindowTitle);
  489.     OSErr            myErr = noErr;
  490.  
  491.     if (!readingExtension) {
  492.         myLength = QTJPEG_ReadWord(theRefNum);
  493.         QTJPEG_ReadByte(theRefNum);
  494.         myHeight = QTJPEG_ReadWord(theRefNum);
  495.         myWidth = QTJPEG_ReadWord(theRefNum);
  496.  
  497.         // make sure we do have something to display
  498.         if ((myWidth != 0) && (myHeight != 0)) {
  499.         
  500.             // now set up the image description
  501.             (*theDesc)->idSize             = sizeof(ImageDescription);
  502.             (*theDesc)->cType             = FOUR_CHAR_CODE('jpeg');
  503.             (*theDesc)->dataRefIndex     = 0;
  504.             (*theDesc)->revisionLevel     = 0;
  505.             (*theDesc)->vendor             = 0;
  506.             (*theDesc)->temporalQuality = 0;
  507.             (*theDesc)->spatialQuality    = codecNormalQuality;
  508.             (*theDesc)->width             = myWidth;
  509.             (*theDesc)->height             = myHeight;
  510.             (*theDesc)->frameCount         = 1;
  511.             BlockMove(myTitle, (*theDesc)->name, 13);
  512.             (*theDesc)->clutID             = -1;
  513.             
  514.             myComponents = QTJPEG_ReadByte(theRefNum);
  515.             
  516.             switch (myComponents) {
  517.                 case 1:        
  518.                     (*theDesc)->depth = 40;
  519.                     break;
  520.  
  521.                 case 3:
  522.                     (*theDesc)->depth = 32;
  523.                     break;
  524.  
  525.                 case 4:
  526.                     (*theDesc)->depth = 32;
  527.                     break;
  528.                                         
  529.                 default:
  530.                     myErr = paramErr;
  531.                     return(myErr);
  532.                     break;
  533.             }
  534.             
  535.             myErr = SetFPos(theRefNum, fsFromMark, myLength - 8);
  536.             return(noErr);
  537.         }
  538.         
  539.     } else {
  540.         myLength = QTJPEG_ReadWord(theRefNum) - 2;
  541.         myErr = SetFPos(theRefNum, fsFromMark, myLength);
  542.         if (myErr != noErr)
  543.             return(myErr);
  544.     }
  545.     
  546.     free(myTitle);
  547.  
  548.     return(myErr);
  549. }
  550.  
  551.  
  552. //////////
  553. //
  554. // QTJPEG_HandleSOSMarker
  555. // Handle an SOS marker in the specified file.
  556. //
  557. //////////
  558.  
  559. void QTJPEG_HandleSOSMarker (long theRefNum)
  560. {
  561.     short        myComponents;
  562.     short        myWord;
  563.     
  564.     QTJPEG_ReadWord(theRefNum);
  565.     myComponents = QTJPEG_ReadByte(theRefNum);
  566.     
  567.     for (myWord = 0; myWord < myComponents; myWord++)
  568.         QTJPEG_ReadWord(theRefNum);
  569. }
  570.  
  571.  
  572. //////////
  573. //
  574. // QTJPEG_ReadByte
  575. // Read the next byte from the specified file.
  576. //
  577. //////////
  578.  
  579. UInt8 QTJPEG_ReadByte (short theRefNum)
  580. {
  581.     UInt8        myData;
  582.     long        myBytesNeeded = sizeof(char);
  583.  
  584.     (void)FSRead(theRefNum, &myBytesNeeded, &myData);
  585.     return(myData);
  586. }
  587.  
  588.  
  589. //////////
  590. //
  591. // QTJPEG_ReadWord
  592. // Read the next word from the specified file.
  593. //
  594. //////////
  595.  
  596. UInt16 QTJPEG_ReadWord (short theRefNum)
  597. {
  598.     UInt16        myData;
  599.     long        myBytesNeeded = sizeof(UInt16);
  600.  
  601.     (void)FSRead(theRefNum, &myBytesNeeded, &myData);
  602.     
  603.     // in JPEG files, the data is stored in a big-endian order
  604.     myData = EndianU16_BtoN(myData);
  605.     return(myData);
  606. }
  607.  
  608.  
  609. //////////
  610. //
  611. // QTJPEG_SkipLength
  612. // Skip over the length word.
  613. //
  614. //////////
  615.  
  616. void QTJPEG_SkipLength (long theRefNum)
  617. {
  618.     UInt16        mySkip;
  619.     
  620.     mySkip = QTJPEG_ReadWord(theRefNum) - 2;
  621.     SetFPos(theRefNum, fsFromMark, mySkip);
  622. }
  623.  
  624.  
  625. //////////
  626. //
  627. // QTJPEG_NewJPEGWorld
  628. // Return, through the theWorld parameter, a new offscreen graphics world suitable
  629. // for drawing a JPEG image of the specified bitdepth into.
  630. //
  631. //////////
  632.  
  633. OSErr QTJPEG_NewJPEGWorld (GWorldPtr *theWorld, short theDepth, Rect theRect)
  634. {
  635.     GWorldPtr        oldPort;
  636.     GDHandle        oldGD;
  637.     PixMapHandle    myPixMap;
  638.     CTabHandle        myCTab = NULL;
  639.     OSErr            myErr = paramErr;
  640.     
  641.     if (theWorld != NULL) {
  642.         // save the current graphics port
  643.         GetGWorld(&oldPort, &oldGD);
  644.     
  645.         // if depth is greater than 32, then the image is grayscale
  646.         if (theDepth > 32) {
  647.             myCTab = GetCTable(theDepth);
  648.             theDepth = theDepth - 32;
  649.         }
  650.         
  651.         // first try to allocate a GWorld in the application's heap
  652.         myErr = NewGWorld(theWorld, theDepth, &theRect, myCTab, NULL, 0L);
  653.         
  654.         // otherwise, try to allocate a GWorld in temporary memory
  655.         if (myErr != noErr)
  656.             myErr = NewGWorld(theWorld, theDepth, &theRect, myCTab, NULL, useTempMem);
  657.     
  658.         if ((myErr == noErr) && (theWorld != NULL))  {
  659.             myPixMap = GetGWorldPixMap(*theWorld);
  660.             if (LockPixels(myPixMap)) {
  661.                 SetGWorld(*theWorld, NULL);
  662.                 EraseRect(&theRect);
  663.                 UnlockPixels(myPixMap);
  664.             }
  665.         }
  666.         
  667.         SetGWorld(oldPort, oldGD);    
  668.     }
  669.     
  670.     return(myErr);
  671. }
  672.  
  673.  
  674. //////////
  675. //
  676. // QTJPEG_SaveJPEG
  677. // Save the specified image as a compressed file.
  678. //
  679. //////////
  680.  
  681. OSErr QTJPEG_SaveJPEG (GWorldPtr theWorld)
  682. {
  683.     StandardFileReply            myReply;
  684.     ImageDescriptionHandle         myDesc;
  685.     Handle                        myData;
  686.     Rect                        myRect;
  687.     PixMapHandle                myPixMap;    
  688.     CTabHandle                    myCTab = NULL;
  689.     ICMFlushProcRecord            myFlushProc;    
  690.     short                        myRefNum;
  691.     short                        myDepth;
  692.     StringPtr                     myImagePrompt = QTUtils_ConvertCToPascalString(kSaveImagePrompt);
  693.     StringPtr                     myImageFileName = QTUtils_ConvertCToPascalString(kSaveImageFileName);
  694.     OSErr                        myErr = paramErr;
  695.     
  696.     if (theWorld == NULL)
  697.         goto bail;
  698.     
  699.     // have the user select the name of the new image file
  700.     StandardPutFile(myImagePrompt, myImageFileName, &myReply);
  701.     if (!myReply.sfGood)
  702.         goto bail;
  703.     
  704.     myDesc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription));
  705.     if (myDesc == NULL)
  706.         goto bail;
  707.     
  708.     myRect = theWorld->portRect;
  709.     myPixMap = GetGWorldPixMap(theWorld);
  710.     
  711.     if (LockPixels(myPixMap)) {    
  712.     
  713.         // if less than 16-bit then get the color table of our GWorld
  714.         myDepth = (**myPixMap).pixelSize;
  715.         if (myDepth < 16)
  716.             myCTab = (**myPixMap).pmTable;
  717.         
  718.         myData = NewHandle(kBufferSize);
  719.         myErr = MemError();
  720.         
  721.         if ((myData != NULL) && (myErr == noErr)) {
  722.             CodecType            myCodec = kJPEGCodecType;
  723.  
  724.             HLock(myData);
  725.                                                          
  726.             if (myReply.sfReplacing) 
  727.                 myErr = FSpDelete(&myReply.sfFile);
  728.         
  729.             myErr = FSpCreate(&myReply.sfFile, kImageFileCreator, kQTFileTypeJPEG, myReply.sfScript);
  730.             
  731.             if (myErr == noErr)
  732.                 myErr = FSpOpenDF(&myReply.sfFile, fsRdWrPerm, &myRefNum);
  733.                 
  734.             if (myErr == noErr)
  735.                 myErr = SetFPos(myRefNum, fsFromStart, 0);
  736.                 
  737.             if (myErr == noErr) {
  738.                 ICMFlushProcRecordPtr        myFlushProcPtr = NULL;
  739.  
  740.                 myFlushProc.flushProc = NewICMFlushProc(QTJPEG_DataUnloadProc);
  741.                 myFlushProc.flushRefCon = myRefNum;
  742.                 myFlushProcPtr = &myFlushProc;
  743.  
  744.                 // compress the image
  745.                 myErr = FCompressImage(    myPixMap,
  746.                                         &myRect,
  747.                                         myDepth,
  748.                                          codecNormalQuality,
  749.                                          myCodec,
  750.                                         anyCodec,
  751.                                         myCTab,
  752.                                         codecFlagWasCompressed,
  753.                                         kBufferSize,
  754.                                         myFlushProcPtr,
  755.                                         NULL,
  756.                                         myDesc,
  757.                                         *myData);
  758.                 
  759.                 if (myErr == noErr)
  760.                     myErr = SetFPos(myRefNum, fsFromStart, (**myDesc).dataSize);
  761.  
  762.                 if (myErr == noErr)
  763.                     myErr = SetEOF(myRefNum, (**myDesc).dataSize);
  764.                              
  765.                 if (myErr == noErr)        
  766.                     myErr = FSClose(myRefNum);
  767.                 
  768.                 HUnlock(myData);
  769.                 DisposeHandle(myData);
  770.                 
  771.                 DisposeRoutineDescriptor(myFlushProc.flushProc);
  772.             }
  773.         }
  774.     }
  775.     
  776.     UnlockPixels(myPixMap);
  777.     
  778. bail:
  779.     if (myDesc != NULL)
  780.         DisposeHandle((Handle)myDesc);
  781.     
  782.     free(myImagePrompt);
  783.     free(myImageFileName);
  784.  
  785.     return(myErr);
  786. }            
  787.     
  788.     
  789. //////////
  790. //
  791. // QTJPEG_DataUnloadProc
  792. // A data unloading procedure: write the compressed data to disk.
  793. //
  794. // The theRefCon parameter is assumed to be a file reference number of an open file.
  795. //
  796. //////////
  797.  
  798. PASCAL_RTN OSErr QTJPEG_DataUnloadProc (Ptr theData, long theBytesNeeded, long theRefCon)
  799. {
  800.     OSErr        myErr = noErr;
  801.     
  802.     if (theData == NULL) {
  803.         // if data is NULL, set a new position in the file from the current mark, offset by bytesNeeded
  804.         myErr = SetFPos(theRefCon, fsFromMark, theBytesNeeded);
  805.     } else {
  806.         // otherwise, write the specified data to disk
  807.         myErr = FSWrite(theRefCon, &theBytesNeeded, theData);
  808.     }
  809.     
  810.     return(myErr);
  811. }
  812.  
  813.  
  814. //////////
  815. //
  816. // QTJPEG_DataLoadingProc
  817. // A data loading procedure: read the data from disk.
  818. //
  819. // The theRefCon parameter is assumed to be a pointer to our custom data-loading record.
  820. //
  821. //////////
  822.  
  823. PASCAL_RTN OSErr QTJPEG_DataLoadingProc (Ptr *theData, long theBytesNeeded, long theRefCon)
  824. {
  825.     long        myUnusedDataLen;
  826.     long        myNewDataLen;
  827.     DLDataPtr    myDataRec;
  828.     Ptr            myDataPtr;
  829.     OSErr        myErr = noErr;
  830.     
  831.     myDataRec = (DLDataPtr)theRefCon;
  832.     
  833.     if (theData == NULL) {
  834.         myErr = SetFPos(myDataRec->fRefNum, fsFromMark, theBytesNeeded);
  835.     } else {    
  836.         myDataPtr = *theData;
  837.         
  838.         // if QT requests more data than is in the buffer, we will have to load more
  839.         if ((myDataPtr + theBytesNeeded) >= myDataRec->fEndPtr) {
  840.             // move whats left up to the front of the buffer
  841.             myUnusedDataLen = myDataRec->fEndPtr - myDataPtr;
  842.             BlockMove(myDataPtr, myDataRec->fOrigPtr, myUnusedDataLen);
  843.             
  844.             // now fill the buffer with new data,
  845.             // following the data we moved to the front of the buffer
  846.             myNewDataLen = kBufferSize - myUnusedDataLen;
  847.             
  848.             if (myNewDataLen > myDataRec->fFileLength)
  849.                 myNewDataLen = myDataRec->fFileLength;
  850.                 
  851.             myDataPtr = myDataRec->fOrigPtr + myUnusedDataLen;
  852.             
  853.             myErr = FSRead(myDataRec->fRefNum, &myNewDataLen, myDataPtr);
  854.             
  855.             myDataRec->fFileLength -= myNewDataLen;
  856.  
  857.             *theData = myDataRec->fOrigPtr;
  858.         }
  859.     }
  860.     
  861.     return(myErr);
  862. }
  863.